#pragma once

#include <sstream>
#include <fstream>
#include <string>
#include <iomanip>

#include "HashAlgorithm.h"

template <typename T>
class MDAlgorithm : public HashAlgorithm {
public:

    MDAlgorithm() = default;
    MDAlgorithm(const MDAlgorithm&) = delete;
    MDAlgorithm& operator=(const MDAlgorithm&) = delete;
    virtual ~MDAlgorithm() = default;

    virtual std::string getHash(const std::string& filename) const override final {
        char buf[BUFSIZE] = {0};
        std::string result("");

        std::ifstream file(filename.data(), std::ios::in | std::ios::binary);

        if (file.is_open()) {
            T ctx;
            MD_Init(&ctx);

            std::streamsize read = 0;
            do {
                read = file.readsome(buf, BUFSIZE);
                MD_Update(&ctx, buf, read);
            } while (read > 0);

            file.close();

            unsigned char hash[16] = {0};
            MD_Final(hash, &ctx);

            std::stringstream ss;
            for (int i = 0; i < 16; ++i) {
                ss << std::hex << std::setfill('0') << std::setw(2) << (int)hash[i];
            }
            result = ss.str();
    }

    return result;
}

protected:

    virtual int MD_Init(T* ctx) const = 0;
    virtual int MD_Update(T* ctx, const char* data, size_t len) const = 0;
    virtual int MD_Final(unsigned char* md, T* ctx) const = 0;

private:

    static const size_t BUFSIZE = 1024 * 16;
};
